{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Equivalent Pipelines in sktime and skforecast"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Sktime](https://www.sktime.net/en/stable/), a well-known forecasting library, provides functionality to apply transformations to both the target variable and exogenous variables using two distinct classes:\n",
    "\n",
    "- [`TransformedTargetForecaster`](https://www.sktime.net/en/latest/api_reference/auto_generated/sktime.forecasting.compose.TransformedTargetForecaster.html#transformedtargetforecaster): Applies the specified transformations to the target series.\n",
    "\n",
    "- [`ForecastingPipeline`](https://www.sktime.net/en/latest/api_reference/auto_generated/sktime.forecasting.compose.ForecastingPipeline.html): Applies the specified transformations to the exogenous variables before passing them to the forecaster.\n",
    "\n",
    "Similarly, [skforecast supports transformations](../user_guides/sklearn-transformers-and-pipeline.html) for both the target variable and exogenous variables through the following arguments present in all forecasters:\n",
    "\n",
    "- `transformer_y`: Applies the specified transformations (single transformer or a sklearn pipeline with multiple transformers) to the target variable.\n",
    "\n",
    "- `transformer_series`: Equivalent to `transformer_y` in multi-series forecasters.\n",
    "\n",
    "- `transformer_exog`: Applies the specified transformations (single transformer or a sklearn pipeline with multiple transformers) to the exogenous variables.\n",
    "\n",
    "The following document provides a side-by-side comparison of equivalent code in **Sktime** and **Skforecast** for applying transformations to the target variable and exogenous variables."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Without exogenous variables**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table>\n",
    "\n",
    "<tr>\n",
    "    <td style=\"text-align: center;\"><strong>skforecast</strong></td>\n",
    "    <td style=\"text-align: center;\"><strong>sktime</strong></td>\n",
    "</tr>\n",
    "\n",
    "<tr>\n",
    "<td style=\"vertical-align: top;\">\n",
    "\n",
    "```python\n",
    "from skforecast.recursive import ForecasterRecursive\n",
    "from sklearn.linear_model import Ridge\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator     = Ridge(random_state=951),\n",
    "                 lags          = 15,\n",
    "                 transformer_y = StandardScaler(),\n",
    "             )\n",
    "forecaster.fit(y=y)\n",
    "predictios = forecaster.predict(steps=10)\n",
    "predictios\n",
    "```\n",
    "\n",
    "</td>\n",
    "\n",
    "<td style=\"vertical-align: top;\">\n",
    "\n",
    "```python\n",
    "from sklearn.linear_model import Ridge\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sktime.transformations.series.adapt import TabularToSeriesAdaptor\n",
    "from sktime.forecasting.base import ForecastingHorizon\n",
    "from sktime.forecasting.compose import (\n",
    "    make_reduction,\n",
    "    TransformedTargetForecaster,\n",
    ")\n",
    "\n",
    "estimator = make_reduction(Ridge(random_state=951), window_length=15, strategy=\"recursive\")\n",
    "forecaster = TransformedTargetForecaster(\n",
    "    steps=[\n",
    "        (\"boxcox\", TabularToSeriesAdaptor(StandardScaler())),\n",
    "        (\"estimator\", estimator),\n",
    "    ]\n",
    ")\n",
    "forecaster.fit(y=y)\n",
    "fh = ForecastingHorizon(np.arange(1, 11), is_relative=True)\n",
    "predictions = forecaster.predict(fh=fh)\n",
    "predictios\n",
    "```\n",
    "</td>\n",
    "\n",
    "</tr>\n",
    "\n",
    "</table>\n",
    "\n",
    "<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**With exogenous variables**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table>\n",
    "\n",
    "<tr>\n",
    "    <td style=\"text-align: center;\"><strong>skforecast</strong></td>\n",
    "    <td style=\"text-align: center;\"><strong>sktime</strong></td>\n",
    "</tr>\n",
    "\n",
    "<tr>\n",
    "<td style=\"vertical-align: top;\">\n",
    "\n",
    "```python\n",
    "from skforecast.recursive import ForecasterRecursive\n",
    "from sklearn.linear_model import Ridge\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sktime.transformations.series.boxcox import BoxCoxTransformer\n",
    "\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator        = Ridge(random_state=951),\n",
    "                 lags             = 15,\n",
    "                 transformer_y    = BoxCoxTransformer(),\n",
    "                 transformer_exog = StandardScaler()\n",
    "             )\n",
    "forecaster.fit(y=y)\n",
    "predictios = forecaster.predict(steps=10)\n",
    "predictios\n",
    "```\n",
    "\n",
    "</td>\n",
    "\n",
    "<td style=\"vertical-align: top;\">\n",
    "\n",
    "```python\n",
    "from sklearn.linear_model import Ridge\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sktime.transformations.series.boxcox import BoxCoxTransformer\n",
    "from sktime.transformations.series.adapt import TabularToSeriesAdaptor\n",
    "from sktime.forecasting.base import ForecastingHorizon\n",
    "from sktime.forecasting.compose import (\n",
    "    make_reduction,\n",
    "    TransformedTargetForecaster,\n",
    "    ForecastingPipeline,\n",
    ")\n",
    "\n",
    "estimator = make_reduction(Ridge(random_state=951), window_length=15, strategy=\"recursive\")\n",
    "pipe_y = TransformedTargetForecaster(\n",
    "    steps=[\n",
    "        (\"boxcox\", BoxCoxTransformer()),\n",
    "        (\"estimator\", estimator),\n",
    "    ]\n",
    ")\n",
    "pipe_X = ForecastingPipeline(\n",
    "    steps=[\n",
    "        (\"scaler\", TabularToSeriesAdaptor(StandardScaler())),\n",
    "        (\"forecaster\", pipe_y),\n",
    "    ]\n",
    ")\n",
    "pipe_X.fit(y=y, X=exog)\n",
    "fh = ForecastingHorizon(np.arange(1, 11), is_relative=True)\n",
    "predictions = pipe_X.predict(fh=fh, X=exog_test)\n",
    "predictions\n",
    "```\n",
    "</td>\n",
    "\n",
    "</tr>\n",
    "\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"admonition note\" name=\"html-admonition\" style=\"background: rgba(255,145,0,.1); padding-top: 0px; padding-bottom: 6px; border-radius: 8px; border-left: 8px solid #ff9100; border-color: #ff9100; padding-left: 10px; padding-right: 10px\">\n",
    "\n",
    "<p class=\"title\">\n",
    "    <i style=\"font-size: 18px; color:#ff9100; border-color: #ff1744;\"></i>\n",
    "    <b style=\"color: #ff9100;\"> <span style=\"color: #ff9100;\">&#9888;</span> Warning</b>\n",
    "</p>\n",
    "\n",
    "<p>When working with exogenous variables, both libraries apply the same transformations. However, the results differ because <strong>sktime</strong> incorporates the lagged values of the exogenous variables into the underlying training matrices, whereas <strong>skforecast</strong> does not. For example, if 3 lagged values are used and two exogenous variables are included, the underlying training matrices are as follows:</p>\n",
    "\n",
    "<ul>\n",
    "  <li><strong>skforecast</strong>: <code>lag_1</code>, <code>lag_2</code>, <code>lag_3</code>, <code>exog_1</code>, <code>exog_2</code></li>\n",
    "  <li><strong>sktime</strong>: <code>lag_1</code>, <code>lag_2</code>, <code>lag_3</code>, <code>exog_1_lag_1</code>, <code>exog_1_lag_2</code>, <code>exog_1_lag_3</code>, <code>exog_2_lag_1</code>, <code>exog_2_lag_2</code>, <code>exog_2_lag_3</code></li>\n",
    "\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Libraries\n",
    "# ======================================================================================\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from skforecast.datasets import fetch_dataset\n",
    "from sklearn.linear_model import Ridge\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "# skforecast\n",
    "from skforecast.recursive import ForecasterRecursive\n",
    "\n",
    "# sktime\n",
    "from sktime.forecasting.base import ForecastingHorizon\n",
    "from sktime.forecasting.compose import (\n",
    "    make_reduction,\n",
    "    TransformedTargetForecaster,\n",
    "    ForecastingPipeline,\n",
    ")\n",
    "from sktime.transformations.series.boxcox import BoxCoxTransformer\n",
    "from sktime.transformations.series.adapt import TabularToSeriesAdaptor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">╭──────────────────────────────── <span style=\"font-weight: bold\">fuel_consumption</span> ────────────────────────────────╮\n",
       "│ <span style=\"font-weight: bold\">Description:</span>                                                                     │\n",
       "│ Monthly fuel consumption in Spain from 1969-01-01 to 2022-08-01.                 │\n",
       "│                                                                                  │\n",
       "│ <span style=\"font-weight: bold\">Source:</span>                                                                          │\n",
       "│ Obtained from Corporación de Reservas Estratégicas de Productos Petrolíferos and │\n",
       "│ Corporación de Derecho Público tutelada por el Ministerio para la Transición     │\n",
       "│ Ecológica y el Reto Demográfico. https://www.cores.es/es/estadisticas            │\n",
       "│                                                                                  │\n",
       "│ <span style=\"font-weight: bold\">URL:</span>                                                                             │\n",
       "│ https://raw.githubusercontent.com/skforecast/skforecast-                         │\n",
       "│ datasets/main/data/consumos-combustibles-mensual.csv                             │\n",
       "│                                                                                  │\n",
       "│ <span style=\"font-weight: bold\">Shape:</span> 644 rows x 5 columns                                                      │\n",
       "╰──────────────────────────────────────────────────────────────────────────────────╯\n",
       "</pre>\n"
      ],
      "text/plain": [
       "╭──────────────────────────────── \u001b[1mfuel_consumption\u001b[0m ────────────────────────────────╮\n",
       "│ \u001b[1mDescription:\u001b[0m                                                                     │\n",
       "│ Monthly fuel consumption in Spain from 1969-01-01 to 2022-08-01.                 │\n",
       "│                                                                                  │\n",
       "│ \u001b[1mSource:\u001b[0m                                                                          │\n",
       "│ Obtained from Corporación de Reservas Estratégicas de Productos Petrolíferos and │\n",
       "│ Corporación de Derecho Público tutelada por el Ministerio para la Transición     │\n",
       "│ Ecológica y el Reto Demográfico. https://www.cores.es/es/estadisticas            │\n",
       "│                                                                                  │\n",
       "│ \u001b[1mURL:\u001b[0m                                                                             │\n",
       "│ https://raw.githubusercontent.com/skforecast/skforecast-                         │\n",
       "│ datasets/main/data/consumos-combustibles-mensual.csv                             │\n",
       "│                                                                                  │\n",
       "│ \u001b[1mShape:\u001b[0m 644 rows x 5 columns                                                      │\n",
       "╰──────────────────────────────────────────────────────────────────────────────────╯\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>litters</th>\n",
       "      <th>month</th>\n",
       "      <th>year</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>date</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1969-01-01</th>\n",
       "      <td>166875.2129</td>\n",
       "      <td>1</td>\n",
       "      <td>1969</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1969-02-01</th>\n",
       "      <td>155466.8105</td>\n",
       "      <td>2</td>\n",
       "      <td>1969</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1969-03-01</th>\n",
       "      <td>184983.6699</td>\n",
       "      <td>3</td>\n",
       "      <td>1969</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1969-04-01</th>\n",
       "      <td>202319.8164</td>\n",
       "      <td>4</td>\n",
       "      <td>1969</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                litters  month  year\n",
       "date                                \n",
       "1969-01-01  166875.2129      1  1969\n",
       "1969-02-01  155466.8105      2  1969\n",
       "1969-03-01  184983.6699      3  1969\n",
       "1969-04-01  202319.8164      4  1969"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Data\n",
    "# ======================================================================================\n",
    "data = fetch_dataset(name='fuel_consumption')\n",
    "data = data.rename(columns={'Gasolinas': 'litters'})\n",
    "data = data.rename_axis('date')\n",
    "data = data.loc[:'1990-01-01 00:00:00']\n",
    "data = data[['litters']]\n",
    "data['month'] = data.index.month\n",
    "data['year'] = data.index.year\n",
    "display(data.head(4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Train-test dates\n",
    "# ======================================================================================\n",
    "end_train = '1980-01-01 23:59:59'\n",
    "data_train = data.loc[:end_train]\n",
    "data_test  = data.loc[end_train:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sktime"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1980-02-01    430096.815068\n",
       "1980-03-01    472406.420587\n",
       "1980-04-01    509203.559184\n",
       "1980-05-01    495910.509282\n",
       "1980-06-01    518548.672893\n",
       "                  ...      \n",
       "1989-09-01    820033.569581\n",
       "1989-10-01    801291.145367\n",
       "1989-11-01    756075.962331\n",
       "1989-12-01    795345.389792\n",
       "1990-01-01    746317.734572\n",
       "Freq: MS, Name: litters, Length: 120, dtype: float64"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Sktime pipeline\n",
    "# ======================================================================================\n",
    "estimator = make_reduction(Ridge(), window_length=15, strategy=\"recursive\")\n",
    "pipe_y = TransformedTargetForecaster(\n",
    "    steps=[\n",
    "        (\"boxcox\", BoxCoxTransformer()),\n",
    "        (\"estimator\", estimator),\n",
    "    ]\n",
    ")\n",
    "pipe_X = ForecastingPipeline(\n",
    "    steps=[\n",
    "        (\"scaler\", TabularToSeriesAdaptor(StandardScaler())),\n",
    "        (\"forecaster\", pipe_y),\n",
    "    ]\n",
    ")\n",
    "pipe_X.fit(y=data_train['litters'], X=data_train[['month', 'year']])\n",
    "fh = ForecastingHorizon(np.arange(1, len(data_test) + 1), is_relative=True)\n",
    "predictions_sktime = pipe_X.predict(fh=fh, X=data_test[['month', 'year']])\n",
    "predictions_sktime"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Skforecast"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1980-02-01    427508.153706\n",
       "1980-03-01    487904.492766\n",
       "1980-04-01    524565.943847\n",
       "1980-05-01    506245.770327\n",
       "1980-06-01    531938.860717\n",
       "                  ...      \n",
       "1989-09-01    770334.700792\n",
       "1989-10-01    753315.656399\n",
       "1989-11-01    787562.026285\n",
       "1989-12-01    743408.935078\n",
       "1990-01-01    682958.500996\n",
       "Freq: MS, Name: pred, Length: 120, dtype: float64"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Skforecast with transformations\n",
    "# ======================================================================================\n",
    "forecaster = ForecasterRecursive(\n",
    "                 estimator        = Ridge(),\n",
    "                 lags             = 15,\n",
    "                 transformer_y    = BoxCoxTransformer(),\n",
    "                 transformer_exog = StandardScaler()\n",
    "             )\n",
    "forecaster.fit(y=data_train['litters'], exog=data_train[['month', 'year']])\n",
    "\n",
    "predictions_skforecast = forecaster.predict(steps=len(data_test), exog=data_test[['month', 'year']])\n",
    "predictions_skforecast"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>sktime</th>\n",
       "      <th>skforecast</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1980-02-01</th>\n",
       "      <td>430096.815068</td>\n",
       "      <td>427508.153706</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1980-03-01</th>\n",
       "      <td>472406.420587</td>\n",
       "      <td>487904.492766</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1980-04-01</th>\n",
       "      <td>509203.559184</td>\n",
       "      <td>524565.943847</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1980-05-01</th>\n",
       "      <td>495910.509282</td>\n",
       "      <td>506245.770327</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1980-06-01</th>\n",
       "      <td>518548.672893</td>\n",
       "      <td>531938.860717</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1989-09-01</th>\n",
       "      <td>820033.569581</td>\n",
       "      <td>770334.700792</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1989-10-01</th>\n",
       "      <td>801291.145367</td>\n",
       "      <td>753315.656399</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1989-11-01</th>\n",
       "      <td>756075.962331</td>\n",
       "      <td>787562.026285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1989-12-01</th>\n",
       "      <td>795345.389792</td>\n",
       "      <td>743408.935078</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1990-01-01</th>\n",
       "      <td>746317.734572</td>\n",
       "      <td>682958.500996</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>120 rows × 2 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                   sktime     skforecast\n",
       "1980-02-01  430096.815068  427508.153706\n",
       "1980-03-01  472406.420587  487904.492766\n",
       "1980-04-01  509203.559184  524565.943847\n",
       "1980-05-01  495910.509282  506245.770327\n",
       "1980-06-01  518548.672893  531938.860717\n",
       "...                   ...            ...\n",
       "1989-09-01  820033.569581  770334.700792\n",
       "1989-10-01  801291.145367  753315.656399\n",
       "1989-11-01  756075.962331  787562.026285\n",
       "1989-12-01  795345.389792  743408.935078\n",
       "1990-01-01  746317.734572  682958.500996\n",
       "\n",
       "[120 rows x 2 columns]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Transformation results\n",
    "# ======================================================================================\n",
    "results = pd.DataFrame({\n",
    "              'sktime': predictions_sktime,\n",
    "              'skforecast': predictions_skforecast,\n",
    "          })\n",
    "results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Equivalent transformations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following table shows the equivalent transformations in sktime and skforecast:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Box-Cox transformation\n",
    "# ======================================================================================\n",
    "from sktime.transformations.series.boxcox import BoxCoxTransformer\n",
    "from sklearn.preprocessing import PowerTransformer\n",
    "\n",
    "# sktime\n",
    "transformer_sktime = BoxCoxTransformer()\n",
    "y_hat_sktime = transformer_sktime.fit_transform(data_train['litters'])\n",
    "\n",
    "# skforecast\n",
    "transformer_skforecast = PowerTransformer(method='box-cox', standardize=False)\n",
    "y_hat_skforecast = transformer_skforecast.fit_transform(data_train[['litters']]).flatten()\n",
    "\n",
    "np.testing.assert_allclose(y_hat_sktime, y_hat_skforecast)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Differencing\n",
    "# ======================================================================================\n",
    "from sktime.transformations.series.difference import Differencer\n",
    "from skforecast.preprocessing import TimeSeriesDifferentiator\n",
    "\n",
    "# sktime\n",
    "transformer_sktime = Differencer(lags=1)\n",
    "y_hat_sktime = transformer_sktime.fit_transform(data_train['litters'])[1:]\n",
    "\n",
    "# skforecast\n",
    "transformer_skforecast = TimeSeriesDifferentiator(order=1)\n",
    "y_hat_skforecast = transformer_skforecast.fit_transform(data_train['litters'].to_numpy())[1:]\n",
    "\n",
    "np.testing.assert_allclose(y_hat_sktime, y_hat_skforecast)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/joaquin/miniconda3/envs/skforecast_19_py13/lib/python3.13/site-packages/sklearn/utils/validation.py:2749: UserWarning: X does not have valid feature names, but FunctionTransformer was fitted with feature names\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "# Log transformation\n",
    "# ======================================================================================\n",
    "from sklearn.preprocessing import FunctionTransformer\n",
    "from sktime.transformations.series.boxcox import LogTransformer\n",
    "\n",
    "# sktime\n",
    "transformer_sktime = LogTransformer(offset=1)\n",
    "y_hat_sktime = transformer_sktime.fit_transform(data_train['litters'])\n",
    "\n",
    "# skforecast\n",
    "transformer_skforecast = FunctionTransformer(func=np.log1p, inverse_func=np.expm1, validate=True)\n",
    "y_hat_skforecast = transformer_skforecast.fit_transform(data_train[['litters']]).flatten()\n",
    "\n",
    "np.testing.assert_allclose(y_hat_sktime, y_hat_skforecast)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "skforecast_19_py13",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.9"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
